package com.hwtx.form.util;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.arithmetic.Addition;
import net.sf.jsqlparser.expression.operators.arithmetic.Division;
import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication;
import net.sf.jsqlparser.expression.operators.arithmetic.Subtraction;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static com.hwtx.form.util.QueryParserResult.AggFunction.isAggFunction;
import static com.hwtx.form.util.QueryParserResult.NO_NEED_TABLE;
import static com.hwtx.form.util.QueryParserResult.ValueType.*;


@Slf4j
public class QueryParser {

    public static final AtomicInteger functionIndex = new AtomicInteger(1);
    private static final ThreadLocal<TableInfo> tables = new ThreadLocal<>();

    public static void main(String[] args) throws JSQLParserException {
//        String sql = "select distinct merchant_code from mall_merchant_category t where t.cat1 in (@cat1) and t.cat2 in (@cat2)";
//        String sql = "select id,name,age,sum(id) as ss from a where a.id=1 and a.name=@name group by a.id order by ss desc limit 10";
//        String sql = "select a.*,b.* from t1 a left join t2 b on a.id=b.id and a.name=b.name or (a.c=b.c) " +
//                "join t3 on a.id=t3.id and a.name=t3.name " +
//                "where  a.id=1 and a.name=@name group by a.id order by ss desc limit 10";
        String sql = "select count(1) 访问量,log_name 功能 from sys_log where update_time >=@start and update_time <= @end group by log_name order by xc(dd) desc";
        System.out.println(parser(sql));
    }


    public static QueryParserResult parser(String query) throws JSQLParserException {
        PlainSelect plainSelect = (PlainSelect) CCJSqlParserUtil.parse(query);
        QueryParserResult queryParserResult = new QueryParserResult();
        queryParserResult.setRawExpress(query);
        queryParserResult.getProject().setDistinct(plainSelect.getDistinct() != null);
        Context context = new Context();
        context.setQuery(query);
        context.setQueryParserResult(queryParserResult);
        List<Join> joins = plainSelect.getJoins();
        QueryParserResult.SelectTable selectTable = handleFromTable((Table) plainSelect.getFromItem());
        context.getQueryParserResult().setTable(selectTable);
        tables.set(new TableInfo(Map.of(selectTable.getName(), selectTable.getAlias() == null ? "" : selectTable.getAlias()), selectTable));
        Set<String> containedAlias = Sets.newHashSet();
        context.setData(containedAlias);
        plainSelect.getSelectItems().forEach(item -> handleSelectItem(item, context));
        if (plainSelect.getWhere() != null) {
            handleWhere(plainSelect.getWhere(), context);
        }
        if (plainSelect.getGroupBy() != null) {
            handleGroupBy(plainSelect.getGroupBy(), context);
        }
        if (plainSelect.getOrderByElements() != null) {
            handleOrderBy(plainSelect.getOrderByElements(), queryParserResult);
        }
        if (plainSelect.getLimit() != null) {
            handleLimit(plainSelect.getLimit(), plainSelect.getOffset(), queryParserResult);
        }
        if (CollectionUtils.isNotEmpty(joins)) {
            joins.forEach(join -> handleJoin(join, context));
        }
        return queryParserResult;
    }

    private static void handleJoin(Join join, Context context) {
        Table table = (Table) join.getFromItem();
        QueryParserResult.JoinDef joinDef = new QueryParserResult.JoinDef();
        joinDef.setTable(handleFromTable(table));
        joinDef.setInner(join.isInner());
        joinDef.setLeft(join.isLeft());
        joinDef.setRight(join.isRight());
        tables.set(new TableInfo(Map.of(joinDef.getTable().getName(), joinDef.getTable().getAlias() == null ? "" : joinDef.getTable().getAlias()), joinDef.getTable()));
        join.getOnExpressions().forEach(expression -> {
            Context joinContext = new Context();
            joinContext.setQueryParserResult(new QueryParserResult());
            handleWhere(expression, joinContext);
            joinDef.setOnFilterRoot(joinContext.getQueryParserResult().getFilterRoot());
        });
        context.getQueryParserResult().getJoins().add(joinDef);
    }

    private static void handleSelectItem(SelectItem<?> item, Context context) {
        item.accept(new SelectItemVisitorAdapter<>() {
            @Override
            public <S> Object visit(SelectItem<? extends Expression> selectItem, S ctx) {
                Alias alias = selectItem.getAlias();
                String aliasName = alias != null ? alias.getName() : null;
                if (alias != null && ((Set<?>) context.getData()).contains(alias.getName())) {
                    throw Exceptions.DS_CONFIG_ERROR("别名已被设置, alias = " + selectItem.getAlias().getName());
                }
                Expression expression = selectItem.getExpression();
                Map<String, ChildrenNode<QueryParserResult.SelectItem>> mapping = Maps.newHashMap();

                BinaryNode<QueryParserResult.SelectItem> functionsRoot = new BinaryNode<>();
                if (expression instanceof BinaryExpression) {
                    context.getQueryParserResult().getProject().getColumns().add(QueryParserResult.FunctionsSelectItem.builder().alias(aliasName).functionsRoot(functionsRoot).selectType(QueryParserResult.SelectType.FUNCTIONS).build());
                }
                Set<QueryParserResult.Item> functionParameters = Sets.newHashSet();
                expression.accept(new ExpressionVisitorAdapter<Void>() {
                    private boolean isFunParameter(Object name) {
                        return functionParameters.stream().noneMatch(item -> Objects.equals(item.getValue(), name));
                    }

                    @Override
                    public Void visit(AllColumns allColumns, Object s) {
                        context.getQueryParserResult().getProject().getColumns().add(QueryParserResult.AllSelectItem.builder().selectType(QueryParserResult.SelectType.ALL).build());
                        return super.visit(allColumns, s);
                    }

                    @Override
                    public Void visit(AllTableColumns allColumns, Object s) {
                        context.getQueryParserResult().getProject().getColumns().add(QueryParserResult.AllSelectItem.builder()
                                .selectType(QueryParserResult.SelectType.ALL).alias(allColumns.getTable().getFullyQualifiedName()).build());
                        return super.visit(allColumns, s);
                    }

                    @Override
                    public Void visit(Function function, Object s) {
                        QueryParserResult.FunctionDef functionDef = handleFunction(function, context);
                        QueryParserResult.FunctionSelectItem functionSelectItem = QueryParserResult.FunctionSelectItem.builder().function(functionDef).alias(aliasName).selectType(functionDef.getSelectType()).item(function.toString()).build();
                        functionParameters.addAll(functionSelectItem.getFunction().getParameters());
                        if (mapping.containsKey(function.toString())) {
                            functionDef.setFactor(true);
                            functionSelectItem.setAlias(aliasName + "_" + functionIndex.getAndIncrement());
                            updateSelectTree(functionSelectItem, mapping.get(function.toString()));
                        } else {
                            context.getQueryParserResult().getProject().getColumns().add(functionSelectItem);
                        }
                        return super.visit(function, s);
                    }

                    @Override
                    public Void visit(Column column, Object s) {
                        if (isFunParameter(column.getColumnName())) {
                            String item = column.getColumnName();
                            QueryParserResult.StrSelectItem strSelectItem = QueryParserResult.StrSelectItem.builder().item(item).alias(aliasName)
                                    .selectType(QueryParserResult.SelectType.STR)
                                    .table(column.getTable() == null ? null : column.getTable().getName())
                                    .build();
                            if (mapping.containsKey(column.toString())) {
                                updateSelectTree(strSelectItem, mapping.get(column.toString()));
                            } else {
                                context.getQueryParserResult().getProject().getColumns().add(strSelectItem);
                            }
                        }
                        return super.visit(column, s);
                    }

                    @Override
                    public Void visit(Addition addition, Object s) {
                        buildSelectItem(addition, aliasName, functionsRoot, mapping);
                        return super.visit(addition, s);
                    }

                    @Override
                    public Void visit(Division division, Object s) {
                        buildSelectItem(division, aliasName, functionsRoot, mapping);
                        return super.visit(division, s);
                    }

                    @Override
                    public Void visit(Multiplication multiplication, Object s) {
                        buildSelectItem(multiplication, aliasName, functionsRoot, mapping);
                        return super.visit(multiplication, s);
                    }

                    @Override
                    public Void visit(Subtraction subtraction, Object s) {
                        buildSelectItem(subtraction, aliasName, functionsRoot, mapping);
                        return super.visit(subtraction, s);
                    }

                    @Override
                    public Void visit(DoubleValue value, Object s) {
                        if (isFunParameter(value.getValue())) {
                            QueryParserResult.DoubleSelectItem doubleSelectItem = QueryParserResult.DoubleSelectItem.builder().value(value.getValue()).alias(aliasName).selectType(QueryParserResult.SelectType.NUM_DOUBLE).build();
                            if (mapping.containsKey(value.toString())) {
                                updateSelectTree(doubleSelectItem, mapping.get(value.toString()));
                            } else {
                                context.getQueryParserResult().getProject().getColumns().add(doubleSelectItem);
                            }
                        }
                        return super.visit(value, s);
                    }

                    @Override
                    public Void visit(LongValue value, Object s) {
                        if (isFunParameter(value.getValue())) {
                            QueryParserResult.LongSelectItem longSelectItem = QueryParserResult.LongSelectItem.builder().value(value.getValue()).alias(aliasName).selectType(QueryParserResult.SelectType.NUM_LONG).build();
                            if (mapping.containsKey(value.toString())) {
                                updateSelectTree(longSelectItem, mapping.get(value.toString()));
                            } else {
                                context.getQueryParserResult().getProject().getColumns().add(longSelectItem);
                            }
                        }
                        return super.visit(value, s);
                    }
                }, ctx);
                return null;
            }
        }, context);
    }

    private static QueryParserResult.SelectTable handleFromTable(Table table) {
        return QueryParserResult.SelectTable.builder().name(table.getFullyQualifiedName())
                .ds("mysql").alias(table.getAlias() != null ? table.getAlias().getName() : null).build();
    }

    private static void handleLimit(Limit limit, Offset offset, QueryParserResult queryParserResult) {
        QueryParserResult.Limit limitResult = new QueryParserResult.Limit();
        Expression limitOffset = limit.getOffset();
        if (offset != null && limitOffset != null) {
            throw Exceptions.DS_CONFIG_ERROR("limit offset配置有误，DSL = " + queryParserResult.getRawExpress());
        }
        if (offset != null) {
            QueryParserResult.Item offsetItem = getItemValue(offset.getOffset(), offset.toString());
            limitResult.setOffset(offsetItem);
            if (offsetItem.isVariable()) {
                queryParserResult.getVars().computeIfAbsent(NO_NEED_TABLE, k -> new HashSet<>())
                        .add(QueryParserResult.VarInfo.builder().varName(offsetItem.getValue().toString()).type(Long.class).build());
            }
        } else if (limitOffset != null) {
            QueryParserResult.Item limitItem = getItemValue(limitOffset, limitOffset.toString());
            if (limitItem.isVariable()) {
                queryParserResult.getVars().computeIfAbsent(NO_NEED_TABLE, k -> new HashSet<>())
                        .add(QueryParserResult.VarInfo.builder().varName(limitItem.getValue().toString()).type(Long.class).build());
            }
            limitResult.setOffset(limitItem);
        }

        Expression rowCount = limit.getRowCount();
        QueryParserResult.Item rowCountItem = getItemValue(rowCount, rowCount.toString());
        if (rowCountItem.isVariable()) {
            queryParserResult.getVars().computeIfAbsent(NO_NEED_TABLE, k -> new HashSet<>())
                    .add(QueryParserResult.VarInfo.builder().varName(rowCountItem.getValue().toString()).type(Long.class).build());
        }
        limitResult.setRowCount(rowCountItem);
        queryParserResult.setLimit(limitResult);
    }

    private static void handleOrderBy(final List<OrderByElement> elements, final QueryParserResult queryParserResult) {

        QueryParserResult.OrderBy orderByResult = new QueryParserResult.OrderBy();

        elements.forEach(orderBy -> {
            QueryParserResult.OrderItem orderItem = new QueryParserResult.OrderItem();
            Expression expression = orderBy.getExpression();
            if (expression instanceof Column) {
                orderItem.setValue(QueryParserResult.Item.builder().value(((Column) expression).getColumnName()).valueType(STR).build());
            } else if (expression instanceof UserVariable) {
                //JsqlParser解析结果把用户变量的@符号去掉了，可以通过返回的数据类型是UserVariable区分是否有变量
                String varName = ((UserVariable) expression).getName();
                queryParserResult.getVars().computeIfAbsent(NO_NEED_TABLE, k -> new HashSet<>())
                        .add(QueryParserResult.VarInfo.builder().varName(varName).type(String.class).build());
                orderItem.setValue(QueryParserResult.Item.builder().value(varName).valueType(STR).variable(true).build());
            } else if (expression instanceof Function function) {
                orderItem.setValue(QueryParserResult.Item.builder().value(function.toString()).valueType(FUNCTION).build());
            }
            orderItem.setAsc(orderBy.isAsc());
            orderByResult.getItems().add(orderItem);
        });
        queryParserResult.setOrderBy(orderByResult);
    }

    private static void handleGroupBy(final GroupByElement groupBy, final Context context) {
        if (groupBy.getGroupByExpressionList() != null) {

            QueryParserResult.GroupBy groupByResult = new QueryParserResult.GroupBy();
            groupBy.getGroupByExpressionList().accept(new ExpressionVisitorAdapter<Void>() {
                public <S> Void visit(ExpressionList<? extends Expression> expressionList, S s) {
                    expressionList.forEach(expression -> {
                        if (expression instanceof Column) {
                            groupByResult.getItems().add(QueryParserResult.Item.builder().value(((Column) expression).getColumnName()).valueType(STR).build());
                        } else if (expression instanceof UserVariable) {
                            String varName = ((UserVariable) expression).getName();
                            context.getQueryParserResult().getVars().computeIfAbsent(NO_NEED_TABLE, k -> new HashSet<>())
                                    .add(QueryParserResult.VarInfo.builder().varName(varName).type(String.class).build());
                            groupByResult.getItems().add(QueryParserResult.Item.builder().value(varName).valueType(STR).variable(true).build());
                        }
                    });
                    return super.visit(expressionList, s);
                }
            });
            context.getQueryParserResult().setGroupBy(groupByResult);
        } else {
            log.warn("groupBy表达式无效，express = {}", groupBy);
        }
    }

    private static void handleWhere(final Expression where, final Context context) {
        final BinaryNode<QueryParserResult.Filter> root = new BinaryNode<>();
        Map<String, ChildrenNode<QueryParserResult.Filter>> mapping = Maps.newHashMap();
        QueryParserResult queryParserResult = context.getQueryParserResult();
        queryParserResult.setFilterRoot(root);
        where.accept(new ExpressionVisitorAdapter<Void>() {
            @Override
            public Void visit(NotEqualsTo expr, Object s) {
                Expression leftExpression = expr.getLeftExpression();
                Expression rightExpression = expr.getRightExpression();

                if (leftExpression instanceof Column) {
                    QueryParserResult.NotEqualFilter notEqualFilter = new QueryParserResult.NotEqualFilter();
                    notEqualFilter.setExpress(expr.toString());
                    notEqualFilter.setColumn(((Column) leftExpression).getColumnName());
                    QueryParserResult.Item item = getColumnValue(rightExpression, expr.toString());
                    if (item.isVariable()) {
                        queryParserResult.getVars().computeIfAbsent(getTable((Column) leftExpression), k -> new HashSet<>())
                                .add(QueryParserResult.VarInfo.builder().varName(item.getValue().toString()).column(notEqualFilter.getColumn()).build());
                    }
                    notEqualFilter.setValue(item);
                    updateFilterTree(root, notEqualFilter, expr.toString(), mapping);
                    queryParserResult.addFilterVar(notEqualFilter);
                } else {
                    throw Exceptions.DS_CONFIG_ERROR("not equals过滤器左侧为非Column类型, express = " + expr);
                }
                return super.visit(expr, s);
            }

            @Override
            public Void visit(AndExpression expr, Object s) {
                buildFilter(expr, mapping, root);
                return super.visit(expr, s);
            }

            @Override
            public Void visit(OrExpression expr, Object s) {
                buildFilter(expr, mapping, root);
                return super.visit(expr, s);
            }

            @Override
            public Void visit(EqualsTo expr, Object s) {
                Expression leftExpression = expr.getLeftExpression();
                Expression rightExpression = expr.getRightExpression();

                if (leftExpression instanceof Column) {
                    QueryParserResult.EqualFilter equalFilter = new QueryParserResult.EqualFilter();
                    equalFilter.setExpress(expr.toString());
                    equalFilter.setColumn(((Column) leftExpression).getColumnName());
                    QueryParserResult.Item item = getColumnValue(rightExpression, expr.toString());
                    equalFilter.setValue(item);
                    if (item.isVariable()) {
                        queryParserResult.getVars().computeIfAbsent(getTable((Column) leftExpression), k -> new HashSet<>())
                                .add(QueryParserResult.VarInfo.builder().varName(item.getValue().toString()).column(equalFilter.getColumn()).build());
                    }
                    updateFilterTree(root, equalFilter, expr.toString(), mapping);
                    queryParserResult.addFilterVar(equalFilter);
                } else {
                    throw Exceptions.DS_CONFIG_ERROR("equal to过滤器左侧为非Column类型, express = " + expr);
                }
                return super.visit(expr, s);
            }

            @Override
            public Void visit(Between expr, Object s) {
                QueryParserResult.BetweenFilter betweenFilter = new QueryParserResult.BetweenFilter();
                Expression leftExpression = expr.getLeftExpression();
                if (leftExpression instanceof Column) {
                    betweenFilter.setColumn(((Column) leftExpression).getColumnName());
                    betweenFilter.setExpress(expr.toString());
                    betweenFilter.setStart(getItemValue(expr.getBetweenExpressionStart(), expr.toString()));
                    if (betweenFilter.getStart().isVariable()) {
                        queryParserResult.getVars().computeIfAbsent(getTable((Column) leftExpression), k -> new HashSet<>())
                                .add(QueryParserResult.VarInfo.builder().varName(betweenFilter.getStart().getValue().toString())
                                        .column(betweenFilter.getColumn()).build());
                    }
                    betweenFilter.setEnd(getItemValue(expr.getBetweenExpressionEnd(), expr.toString()));
                    if (betweenFilter.getEnd().isVariable()) {
                        queryParserResult.getVars().computeIfAbsent(getTable((Column) leftExpression), k -> new HashSet<>())
                                .add(QueryParserResult.VarInfo.builder().varName(betweenFilter.getEnd().getValue().toString())
                                        .column(betweenFilter.getColumn()).build());
                    }
                    updateFilterTree(root, betweenFilter, expr.toString(), mapping);
                    queryParserResult.addFilterVar(betweenFilter);
                } else {
                    throw Exceptions.DS_CONFIG_ERROR("between过滤器左侧为非Column类型, express = " + expr);
                }
                return super.visit(expr, s);
            }

            @Override
            public Void visit(GreaterThan expr, Object s) {
                handleMinorAndGreater(expr, new QueryParserResult.GreaterThanFilter(), queryParserResult, mapping, root);
                return super.visit(expr, s);
            }

            @Override
            public Void visit(GreaterThanEquals expr, Object s) {
                handleMinorAndGreater(expr, new QueryParserResult.GreaterThanEqualsFilter(), queryParserResult, mapping, root);
                return super.visit(expr, s);
            }

            @Override
            public Void visit(LikeExpression expr, Object s) {
                QueryParserResult.LikeFilter likeFilter = new QueryParserResult.LikeFilter();
                Expression leftExpression = expr.getLeftExpression();
                Expression rightExpression = expr.getRightExpression();
                if (rightExpression == null) {
                    throw Exceptions.DS_CONFIG_ERROR("like过滤器内容不可为空, express = " + expr);
                }
                if (leftExpression instanceof Column) {
                    likeFilter.setExpress(expr.toString());
                    likeFilter.setColumn(((Column) leftExpression).getColumnName());
                    if (rightExpression instanceof StringValue) {
                        String value = ((StringValue) rightExpression).getValue();
                        if (StringUtils.isEmpty(value)) {
                            throw Exceptions.DS_CONFIG_ERROR("like过滤器内容不存在, express = " + expr);
                        }
                        likeFilter.setValue(QueryParserResult.Item.builder().value(value).valueType(STR).build());
                    } else if (rightExpression instanceof UserVariable) {
                        String varName = ((UserVariable) rightExpression).getName();
                        queryParserResult.getVars().computeIfAbsent(getTable((Column) leftExpression), k -> new HashSet<>())
                                .add(QueryParserResult.VarInfo.builder().varName(varName).column(likeFilter.getColumn()).build());
                        likeFilter.setValue(QueryParserResult.Item.builder().value(varName).valueType(STR).variable(true).build());
                    }
                    updateFilterTree(root, likeFilter, expr.toString(), mapping);
                    queryParserResult.addFilterVar(likeFilter);
                } else {
                    throw Exceptions.DS_CONFIG_ERROR("like过滤器左侧为非Column类型, express = " + expr);
                }
                return super.visit(expr, s);
            }

            @Override
            public Void visit(MinorThan expr, Object s) {
                handleMinorAndGreater(expr, new QueryParserResult.LessThanFilter(), queryParserResult, mapping, root);
                return super.visit(expr, s);
            }

            @Override
            public Void visit(MinorThanEquals expr, Object s) {
                handleMinorAndGreater(expr, new QueryParserResult.LessThanEqualsFilter(), queryParserResult, mapping, root);
                return super.visit(expr, s);
            }

            @Override
            public Void visit(InExpression expr, Object s) {
                QueryParserResult.InFilter inFilter = new QueryParserResult.InFilter();
                inFilter.setExpress(expr.toString());
                if (expr.getLeftExpression() instanceof Column) {
                    inFilter.setColumn(((Column) expr.getLeftExpression()).getColumnName());
                    if (expr.getRightExpression() != null) {
                        expr.getRightExpression().accept(new ExpressionVisitorAdapter<Void>() {
                            @Override
                            public <S> Void visit(ExpressionList<?> expressionList, S s) {
                                expressionList.forEach(expression -> {
                                    QueryParserResult.Item item = getItemValue(expression, expr.toString());
                                    if (item.isVariable()) {
                                        queryParserResult.getVars().computeIfAbsent(getTable((Column) expr.getLeftExpression()), k -> new HashSet<>())
                                                .add(QueryParserResult.VarInfo.builder().varName(item.getValue().toString())
                                                        .column(inFilter.getColumn()).build());
                                    }
                                    inFilter.getValues().add(item);
                                });
                                return super.visit(expressionList, s);
                            }
                        });
                    } else {
                        throw Exceptions.DS_CONFIG_ERROR("IN表达式内容不存在，express = " + expr);
                    }
                } else {
                    throw Exceptions.DS_CONFIG_ERROR("In不能支持非Column类型,express = " + expr);
                }
                updateFilterTree(root, inFilter, expr.toString(), mapping);
                queryParserResult.addFilterVar(inFilter);
                return super.visit(expr, s);
            }

            @Override
            public Void visit(IsNullExpression expr, Object s) {
                QueryParserResult.ColumnFilter columnFilter = !expr.isNot() ? new QueryParserResult.IsNullFilter() : new QueryParserResult.IsNotNullFilter();
                columnFilter.setExpress(expr.toString());
                if (expr.getLeftExpression() instanceof Column) {
                    columnFilter.setColumn(((Column) expr.getLeftExpression()).getColumnName());
                    queryParserResult.addFilterVar(columnFilter);
                } else {
                    throw Exceptions.DS_CONFIG_ERROR("IsNull/IsNotNull不支持非Column类型,express = " + expr);
                }
                updateFilterTree(root, columnFilter, expr.toString(), mapping);
                return super.visit(expr, s);
            }

            @Override
            public Void visit(Function function, Object s) {
                QueryParserResult.FunctionDef functionDef = handleFunction(function, context);
                QueryParserResult.FunctionFilter functionFilter = new QueryParserResult.FunctionFilter();
                functionFilter.setFunction(functionDef);
                updateFilterTree(root, functionFilter, function.toString(), mapping);
                queryParserResult.addFilterVar(functionFilter);
                return super.visit(function, s);
            }
        });
    }

    private static void handleMinorAndGreater(BinaryExpression expr, QueryParserResult.GreaterAndMinorFilter filter, QueryParserResult queryParserResult, Map<String, ChildrenNode<QueryParserResult.Filter>> mapping, BinaryNode<QueryParserResult.Filter> root) {
        Expression leftExpression = expr.getLeftExpression();
        Expression rightExpression = expr.getRightExpression();
        if (leftExpression instanceof Column) {
            filter.setExpress(expr.toString());
            filter.setColumn(((Column) leftExpression).getColumnName());
            filter.setValue(getItemValue(rightExpression, expr.toString()));
            if (filter.getValue().isVariable()) {
                queryParserResult.getVars().computeIfAbsent(getTable((Column) leftExpression), k -> new HashSet<>())
                        .add(QueryParserResult.VarInfo.builder().varName(filter.getValue().getValue().toString())
                                .column(filter.getColumn()).build());
            }
            updateFilterTree(root, filter, expr.toString(), mapping);
            queryParserResult.addFilterVar(filter);
        } else {
            throw Exceptions.DS_CONFIG_ERROR("MinorAndGreater过滤器左侧为非Column类型, express = " + expr);
        }
    }

    private static void updateSelectTree(QueryParserResult.SelectItem selectItem, ChildrenNode<QueryParserResult.SelectItem> childrenNode) {
        if (childrenNode != null) {
            BinaryNode<QueryParserResult.SelectItem> parent = childrenNode.getParent();
            if (childrenNode.childrenType == BinaryNode.ChildrenType.RIGHT) {
                BinaryNode<QueryParserResult.SelectItem> right = new BinaryNode<>();
                right.setData(selectItem);
                parent.setRight(right);
            } else {
                BinaryNode<QueryParserResult.SelectItem> left = new BinaryNode<>();
                left.setData(selectItem);
                parent.setLeft(left);
            }
        }
    }

    private static void buildSelectItem(BinaryExpression expression, String aliasName, BinaryNode<QueryParserResult.SelectItem> root, Map<String, ChildrenNode<QueryParserResult.SelectItem>> mapping) {
        buildBinaryExpressionTree(expression, root, mapping, new BuildNodeDataCallback<>() {
            @Override
            public QueryParserResult.SelectItem buildNotLeafNodeData(BinaryExpression expression) {
                return QueryParserResult.BinarySelectItem.builder().item(expression.toString()).alias(aliasName).operator(QueryParserResult.Operator.getOp(expression.getStringExpression())).build();
            }

            @Override
            public QueryParserResult.SelectItem buildNodeData(Expression expression) {
                return QueryParserResult.SelectItem.builder().item(expression.toString()).build();
            }

            @Override
            public void setNodeParent(BinaryNode<QueryParserResult.SelectItem> parent, BinaryNode<QueryParserResult.SelectItem> child) {
                QueryParserResult.SelectItem parentSelectItem = parent.getData();
                QueryParserResult.SelectItem childSelectItem = child.getData();
                if (parentSelectItem instanceof QueryParserResult.BinarySelectItem && childSelectItem instanceof QueryParserResult.BinarySelectItem) {
                    ((QueryParserResult.BinarySelectItem) childSelectItem).setParent((QueryParserResult.BinarySelectItem) parentSelectItem);
                }
            }
        });
    }

    private static void buildFilter(BinaryExpression expression, Map<String, ChildrenNode<QueryParserResult.Filter>> mapping, BinaryNode<QueryParserResult.Filter> root) {
        buildBinaryExpressionTree(expression, root, mapping, new BuildNodeDataCallback<>() {
            @Override
            public QueryParserResult.Filter buildNotLeafNodeData(BinaryExpression expression) {
                QueryParserResult.BinaryFilter binaryFilter = new QueryParserResult.BinaryFilter();
                binaryFilter.setExpress(expression.toString());
                binaryFilter.setOp(expression.getStringExpression());
                return binaryFilter;
            }

            @Override
            public QueryParserResult.Filter buildNodeData(Expression expression) {
                QueryParserResult.Filter filter = new QueryParserResult.Filter();
                filter.setExpress(expression.toString());
                return filter;
            }

            @Override
            public void setNodeParent(BinaryNode<QueryParserResult.Filter> parent, BinaryNode<QueryParserResult.Filter> child) {
                QueryParserResult.Filter parentFilter = parent.getData();
                QueryParserResult.Filter childFilter = child.getData();
                if (parentFilter instanceof QueryParserResult.BinaryFilter && childFilter instanceof QueryParserResult.BinaryFilter) {
                    ((QueryParserResult.BinaryFilter) childFilter).setParent((QueryParserResult.BinaryFilter) parentFilter);
                }
            }
        });
    }

    private static void updateFilterTree(BinaryNode<QueryParserResult.Filter> root, QueryParserResult.Filter filter, String express, Map<String, ChildrenNode<QueryParserResult.Filter>> mapping) {

        if (root.getData() == null) {
            root.setData(filter);
        }

        ChildrenNode<QueryParserResult.Filter> childrenNode = mapping.get(express);
        if (childrenNode != null) {
            BinaryNode<QueryParserResult.Filter> parent = childrenNode.getParent();
            if (childrenNode.childrenType == BinaryNode.ChildrenType.RIGHT) {
                BinaryNode<QueryParserResult.Filter> right = new BinaryNode<>();
                right.setData(filter);
                parent.setRight(right);
            } else {
                BinaryNode<QueryParserResult.Filter> left = new BinaryNode<>();
                left.setData(filter);
                parent.setLeft(left);
            }
        }
    }

    private static <T extends Serializable> void buildBinaryExpressionTree(BinaryExpression expression, BinaryNode<T> root, Map<String, ChildrenNode<T>> mapping, BuildNodeDataCallback<T> callback) {
        BinaryNode<T> node;
        T item = callback.buildNotLeafNodeData(expression);
        if (root.getData() == null) {
            root.setData(item);
            node = root;
        } else {
            BinaryNode<T> _node = new BinaryNode<>();
            _node.setData(item);
            node = _node;

            ChildrenNode<T> childrenNode = mapping.get(expression.toString());
            if (childrenNode == null) {
                childrenNode = mapping.get("(" + expression + ")");
            }
            if (childrenNode != null) {
                BinaryNode<T> parent = childrenNode.parent;
                assert parent != null;
                if (childrenNode.childrenType == BinaryNode.ChildrenType.LEFT) {
                    parent.setLeft(node);
                } else {
                    parent.setRight(node);
                }
                callback.setNodeParent(parent, node);
            } else {
                throw Exceptions.DS_CONFIG_ERROR("无法定位过滤表达式, express = " + expression);
            }
        }
        BinaryNode<T> left = new BinaryNode<>();
        left.setData(callback.buildNodeData(expression.getLeftExpression()));
        node.setLeft(left);

        BinaryNode<T> right = new BinaryNode<>();
        right.setData(callback.buildNodeData(expression.getRightExpression()));
        node.setRight(right);

        ChildrenNode<T> leftChildren = new ChildrenNode<>();
        leftChildren.setChildrenType(BinaryNode.ChildrenType.LEFT);
        leftChildren.setParent(node);

        ChildrenNode<T> rightChildren = new ChildrenNode<>();
        rightChildren.setChildrenType(BinaryNode.ChildrenType.RIGHT);
        rightChildren.setParent(node);

        mapping.put(expression.getLeftExpression().toString(), leftChildren);
        mapping.put(expression.getRightExpression().toString(), rightChildren);
    }

    private static QueryParserResult.FunctionDef handleFunction(Function function, Context context) {
        List<QueryParserResult.Item> parameters = getItems(function, context.getQuery());
        Set<String> varNames = parameters.stream().filter(QueryParserResult.Item::isVariable).map(item -> item.getValue().toString()).collect(Collectors.toSet());
        boolean isRouter = false;
        if (isAggFunction(function.getName())) {
            if (!varNames.isEmpty()) {
                throw Exceptions.DS_CONFIG_ERROR("聚合函数暂时不支持参数变量,function = " + function + ", query = " + context.getQuery());
            }
        } else {
            UdfHandler udfHandler = UdfHandler.getUdfHandler(function.getName());
            if (!udfHandler.allowParameterVariable(parameters)) {
                throw Exceptions.DS_CONFIG_ERROR("自定义函数暂时不支持参数变量,function = " + function + ", query = " + context.getQuery());
            }
            if (udfHandler instanceof UdfHandler.RouterUdf) {
                isRouter = true;
            }
        }
        varNames.forEach(varName -> context.getQueryParserResult().getVars().computeIfAbsent(NO_NEED_TABLE,
                k -> new HashSet<>()).add(QueryParserResult.VarInfo.builder().varName(varName).build()));
        QueryParserResult.FunctionDef functionDef = QueryParserResult.FunctionDef.builder().name(function.getName()).distinct(function.isDistinct()).selectType(QueryParserResult.SelectType.getSelectType(function.getName())).parameters(parameters).build();
        if (isRouter) {
            context.getQueryParserResult().setRouterFunction(functionDef);
        }
        return functionDef;
    }

    private static List<QueryParserResult.Item> getItems(Function function, String query) {
        ExpressionList<?> expressionList = function.getParameters();
        List<QueryParserResult.Item> parameters = Lists.newArrayList();
        if (expressionList != null) {
            expressionList.forEach(exp -> {
                if (exp instanceof Function) {
                    throw Exceptions.DS_CONFIG_ERROR("不支持高阶函数,function = " + function + ", query = " + query);
                } else {
                    parameters.add(getColumnValue(exp, exp.toString()));
                }
            });
        }
        return parameters;
    }

    private static QueryParserResult.Item getItemValue(Expression expression, String expressString) {
        return switch (expression) {
            case StringValue stringValue ->
                    QueryParserResult.Item.builder().value(stringValue.getValue()).valueType(STR).build();
            case DoubleValue doubleValue ->
                    QueryParserResult.Item.builder().value(doubleValue.getValue()).valueType(DOUBLE).build();
            case LongValue longValue ->
                    QueryParserResult.Item.builder().value(longValue.getValue()).valueType(LONG).build();
            case UserVariable userVariable ->
                    QueryParserResult.Item.builder().value(userVariable.getName()).valueType(STR).variable(true).build();
            case null, default ->
                    throw Exceptions.DS_CONFIG_ERROR("值类型支持 String,Long,Double,UserVar, express: " + expressString);
        };
    }

    private static QueryParserResult.Item getColumnValue(Expression expression, String exprString) {
        switch (expression) {
            case Column column -> {
                return QueryParserResult.Item.builder().valueType(STR).value(column.getColumnName()).build();
            }
            case StringValue stringValue -> {
                return QueryParserResult.Item.builder().valueType(STR).value(stringValue.getValue()).build();
            }
            case DoubleValue doubleValue -> {
                return QueryParserResult.Item.builder().valueType(DOUBLE).value(doubleValue.getValue()).build();
            }
            case LongValue longValue -> {
                return QueryParserResult.Item.builder().valueType(LONG).value(longValue.getValue()).build();
            }
            case NullValue ignored -> {
                return QueryParserResult.Item.builder().valueType(NULL).value(null).build();
            }
            case SignedExpression signedExpression -> {
                QueryParserResult.Item item = getColumnValue(signedExpression.getExpression(), exprString);
                if (item.getValueType() == DOUBLE) {
                    if (((SignedExpression) expression).getSign() == '-') {
                        return QueryParserResult.Item.builder().valueType(item.getValueType()).value(((Number) item.getValue()).doubleValue() * -1).build();
                    }
                } else if (item.getValueType() == QueryParserResult.ValueType.LONG) {
                    if (((SignedExpression) expression).getSign() == '-') {
                        return QueryParserResult.Item.builder().valueType(item.getValueType()).value(((Number) item.getValue()).longValue() * -1).build();
                    }
                } else if (item.getValueType() == STR) {
                    return QueryParserResult.Item.builder().valueType(item.getValueType()).value(String.valueOf(((SignedExpression) expression).getSign()) + item.getValue()).build();
                }
                return item;
            }
            case UserVariable userVariable -> {
                return QueryParserResult.Item.builder().valueType(STR).value(userVariable.getName()).variable(true).build();
            }
            case null, default ->
                    throw Exceptions.DS_CONFIG_ERROR("过滤器右侧表达式只支持String,Long,Double, express: " + exprString);
        }
    }

    private static String getTable(Column column) {
        if (column.getTable() != null && CollectionUtils.isNotEmpty(column.getTable().getNameParts())) {
            String alias = column.getTable().getNameParts().getFirst();
            if (StringUtils.isNotBlank(alias)) {
                TableInfo tableInfo = tables.get();
                if (tableInfo.tables != null) {
                    for (Map.Entry<String, String> entry : tableInfo.tables.entrySet()) {
                        if (entry.getValue().equals(alias)) {
                            return entry.getKey();
                        }
                    }
                }
            }
        }
        return tables.get().currentTable.getName();
    }

    interface BuildNodeDataCallback<T extends Serializable> {

        T buildNotLeafNodeData(BinaryExpression expression);

        T buildNodeData(Expression children);

        void setNodeParent(BinaryNode<T> parent, BinaryNode<T> child);
    }

    @Data
    static class ChildrenNode<T extends Serializable> {
        BinaryNode<T> parent;
        BinaryNode.ChildrenType childrenType;
    }

    @Data
    static class Context {
        private String query;
        private QueryParserResult queryParserResult;
        private Object data;
    }

    record TableInfo(Map<String, String> tables, QueryParserResult.SelectTable currentTable) {
    }
}
